package com.bzyd.shiro.shiro;

import com.bzyd.shiro.common.Constant;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.LinkedHashMap;

/**
 * shiro 三大核心：
 * 1、subject：用户主体
 * 2、SecurityManager：安全管理器（管理所有的subject，关联Realm）
 * 3、Realm：领域，连接数据的桥梁（连接数据库，用于进行权限信息的验证，一般需要自己实现。）
 */
@Configuration
public class ShiroConfig {

    //凭证匹配器
    @Bean("testCredentialsMatcher")
    public CredentialsMatcher getCredentialsMatcher(){
        HashedCredentialsMatcher cMatcher = new HashedCredentialsMatcher();
        cMatcher.setHashAlgorithmName(Constant.HASH_ALGORITHMNAME);//加密算法
        cMatcher.setHashIterations(Constant.HASH_ITERATIONS);//加密次数
        return cMatcher;
    }

    //重点1：配置自定义领域
    @Bean("testRealm")
    public Realm getRealm(@Qualifier("testCredentialsMatcher") CredentialsMatcher credentialsMatcher) {
        ShiroRealm realm = new ShiroRealm();
        realm.setCredentialsMatcher(credentialsMatcher);
        return realm;
    }

    //内存缓存方式
    @Bean("testCacheManager")
    public CacheManager getCacheManager() {
        return new MemoryConstrainedCacheManager();
    }

    //记住我
    //shiro的记住我是一种基于cookie实现的方式，特定的页面在关掉浏览器后（session消失）也可以进行访问的功能！
    //对需要记住我的页面配置成user权限（anon-无需认证即可访问，authc-需要认证才可访问，user-点击“记住我”功能可访问）
    //authc和user都是需要登录认证才给通过，但如果开启了rememberMe功能的话，user也是可以直接给过的，相当于anon，authc则不行。
    //故我们用user来校验一些非关键操作，比如购买，我们可以采用user校验即可。而支付的时候，我们需要认证的用户，那就需要authc了。
    @Bean("testRememberMeManager")
    public RememberMeManager getRememberMeManager(@Qualifier("testSimpleCookie") Cookie cookie) {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(cookie);
        return cookieRememberMeManager;
    }

    @Bean("testSimpleCookie")
    public Cookie getSimpleCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(10*60);
        return cookie;
    }

    @Bean("testSessionManager")
    public SessionManager getSessionManager() {
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        defaultWebSessionManager.setGlobalSessionTimeout(60*60*1000);
        return defaultWebSessionManager;
    }

    //重点2：配置缓存管理器
    @Bean("testSecurityManager")
    public SecurityManager getSecurityManager(@Qualifier("testRealm") Realm realm,
                                              @Qualifier("testCacheManager") CacheManager cacheManager,
                                              @Qualifier("testRememberMeManager") RememberMeManager rememberMeManager,
                                              @Qualifier("testSessionManager") SessionManager sessionManager) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        defaultWebSecurityManager.setCacheManager(cacheManager);
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager);
        defaultWebSecurityManager.setSessionManager(sessionManager);
        SecurityUtils.setSecurityManager(defaultWebSecurityManager);
        return defaultWebSecurityManager;
    }

    //重点3：配置Filter工厂，设置对应的过滤条件和跳转条件
    @Bean("testShiroFilterFactoryBean")//此处chainName不能为空
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("testSecurityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // setLoginUrl 如果不设置值，默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置无权限时跳转的 url;
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        // 设置拦截器
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 开放登陆接口
        filterChainDefinitionMap.put("/user/login", "anon");
        // 游客，开发权限
        filterChainDefinitionMap.put("/test/test2/**", "anon");

        // 需要角色权限 “管理员”，使用此种方式在无权限时不会报AuthorizationException，会直接跳转上面设置的unauthorized-url
        filterChainDefinitionMap.put("/test/test3/**", "roles[管理员]");
        // 需要权限 “biz:test4”，使用此种方式在无权限时不会报AuthorizationException，会直接跳转上面设置的unauthorized-url
        filterChainDefinitionMap.put("/test/test4/**", "perms[biz:test4]");

        // 注销，执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的url
        filterChainDefinitionMap.put("/user/logout", "logout");

        //其余接口一律拦截
        //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    //ShiroFilter配置
    @Bean
    public FilterRegistrationBean newFilterRegistrationBean() {
        FilterRegistrationBean fBean = new FilterRegistrationBean();
        DelegatingFilterProxy filter = new DelegatingFilterProxy("testShiroFilterFactoryBean");
        fBean.setFilter(filter);
        fBean.addUrlPatterns("/*");
        return fBean;
    }



    /*********以下内容在用到@RequiresPermissions等注解进行权限控制时需要配置-start**********/
    //配置shiro框架中一些bean对象的生命周期管理器
    @Bean("testLifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    //配置代理对象创建器,通过此对象为目标业务对象创建代理对象
    @DependsOn("testLifecycleBeanPostProcessor")
    @Bean("testDefaultAdvisorAutoProxyCreator")
    public DefaultAdvisorAutoProxyCreator newDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }


    //要使用@RequiresPermission等注解的支持，需要开启Shiro-aop注解支持：使用代理方式所以需要开启代码支持
    //加入注解的使用，不加入这个注解不生效
    @Bean("testAuthorizationAttributeSourceAdvisor")
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(@Qualifier("testSecurityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
    /*********以下内容在用到@RequiresPermissions等注解进行权限控制时需要配置-end**********/

}
